﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BreukTrainer_EE_.BreukModel
{
    public delegate Breuk DelegateBreukenBewerking(Breuk b1, Breuk b2);

    /// <summary>
    /// Erft over van <see cref="BreukContainer"/>. Voor alle bewerkingen op Breuken welke een nieuwe <see cref="Breuk"/> opleveren.
    /// </summary>
    /// <remarks>
    /// Door gebruik te maken van delegates kunnen we aan deze klasse allerlei methodes 
    /// die in de klasse Breuk uitgewerkt zijn en die op 2 breuken een bewerking doen 
    /// (optellen, delen, maximum berekenen, ..) die een nieuwe breuk oplevert doorgeven.
    /// Deze klasse hoeft niet te weten welke bewerkingsmethodes er in <see cref="Breuk"/> bestaan.
    /// </remarks>
    public class ProgrammeerbareBreukContainer: BreukContainer
    {
        

        /// <summary>
        /// Voert de bewerking in <paramref name="functie"/> uit op alle Breuken in <see cref="BreukContainer"/>.
        /// </summary>
        /// <param name="functie">De <see cref="DelegateBreukenBewerking"/> met een verwijzing naar een bewerking uit <see cref="Breuk"/></param>
        /// <returns><see cref="Breuk"/></returns>
        public Breuk DoeBewerking(DelegateBreukenBewerking functie)
        {
            return DoeBewerking(functie, Breuken.Count);
        }

        /// <summary>
        /// Voert de bewerking in <paramref name="functie"/> uit op een aantal Breuken in <see cref="BreukContainer"/>.
        /// </summary>
        /// <param name="functie">De <see cref="DelegateBreukenBewerking"/> met een verwijzing naar een bewerking uit <see cref="Breuk"/></param>
        /// <param name="aantal">Een <see langword="integer"/>: het aantal Breuken aan het begin van <see cref="BreukContainer"/>.</param>
        /// <returns><see cref="Breuk"/></returns>
        /// <remarks>Als <paramref name="aantal"/> groter is als aantal breuken in <see cref="BreukContainer"/> worden zelfde breuken hergebruikt.</remarks>
        public Breuk DoeBewerking(DelegateBreukenBewerking functie, int aantal)
        {
            Breuk br = new Breuk(Breuken[0].Teller, Breuken[0].Noemer);
            for (int i = 1; i < aantal; i++)
            {
                br = functie(br, Get(i));
                if (i == Breuken.Count)//mogelijks ook met dubbele for-lus.
                {
                    aantal -= i;
                    i = 0;
                }
            }
            return br;
        }

        /// <summary>
        /// Voert meerdere bewerkingen uit en geeft de formule terug.
        /// </summary>
        /// <param name="functies">Een <see langword="array"/> van <see cref="DelegateBreukenBewerking"/>.</param>
        /// <param name="operanden">Een <see langword="array"/> van <see langword="string"/> operanden.</param>
        /// <returns><see langword="string"/>: de formule.</returns>
        /// <remarks>Kan nog ingewikkelder: ipv van links naar rechts te rekenen, 
        /// rekening houden met volgorde bewerkingen (*&/ eerst).</remarks>
        public String DoeBewerking(DelegateBreukenBewerking[] functies, params string[] operanden)
        {
            if (functies.Length != operanden.Length) return null;
            Breuk br = new Breuk(Breuken[0].Teller, Breuken[0].Noemer);
            String formule = br.ToString();
            int length = functies.Length;
            for(int i = 0; i < length; i++)
            {
                formule += " " + operanden[i] + " " + Breuken[i+1].ToString();
                br = functies[i](br, Breuken[i+1]);
            }
            formule += "=" + br.Vereenvoudig().ToString();
            return formule;
        }
        /// <summary>
        /// Voert meerdere bewerkingen uit en geeft de oplossing terug.
        /// </summary>
        /// <param name="functies">Een <see langword="array"/> van <see cref="DelegateBreukenBewerking"/>.</param>
        /// <param name="operanden">Een <see langword="array"/> van <see langword="string"/> operanden.</param>
        /// <returns><see langword="string"/>: de oplossing.</returns>
        /// <remarks>Kan nog ingewikkelder: ipv van links naar rechts te rekenen, 
        /// rekening houden met volgorde bewerkingen (*&/ eerst).</remarks>
        public String DoeBewerkingResult(params string[] operanden)
        {
            //hier mss nog een extra controle ivm aantal functies
            int length = operanden.Length;
            if (length == 0)
            {
                return Breuken[0].Vereenvoudig().ToString();
            }
            else {
                Breuk[] br = new Breuk[length];
                string[] tmpoperanden=new string[length-1];
                int i=0;
                while(i+1<length && getOperandVal(operanden[i]) < getOperandVal(operanden[i + 1])){
                    tmpoperanden[i] = operanden[i];
                    br[i] = Breuken[i];
                    i++;
                }
                br[i]=ProgrammeerbareBreukContainer.toDelegate(operanden[i])(Breuken[i], Breuken[i + 1]);
                for (int j = i; j+1 < length; j++) {
                    tmpoperanden[j] = operanden[j+1];
                    br[j + 1] = Breuken[j + 2];
                }
                return DoeBewerkingRecursief(br, tmpoperanden);
            }
        }
        public String DoeBewerkingRecursief(Breuk[] breuken, String[] operanden) { 
            int length = operanden.Length;
            if (length == 0)
            {
                return breuken[0].Vereenvoudig().ToString();
            }
            else
            {
                Breuk[] br = new Breuk[length];
                string[] tmpoperanden = new string[length - 1];
                int i = 0;
                while (i + 1 < length && getOperandVal(operanden[i]) < getOperandVal(operanden[i + 1]))
                {
                    tmpoperanden[i] = operanden[i];
                    br[i] = Breuken[i];
                    i++;
                }
                //2 * 3 - 2 * 1/2
                br[i] = ProgrammeerbareBreukContainer.toDelegate(operanden[i])(breuken[i], breuken[i + 1]);
                for (int j = i; j + 1 < length; j++)
                {
                    tmpoperanden[j] = operanden[j + 1];
                    br[j + 1] = breuken[j + 2];
                }
                return DoeBewerkingRecursief(br,tmpoperanden);
            }
        }
        public int getOperandVal(string operand){
            if(operand.Trim()=="-"||operand.Trim()=="+"){
                return 1;
            }
            if (operand.Trim() == ":" || operand.Trim() == "*")
            {
                return 2;
            }
            if (operand.Trim() == "^" || operand.Trim() == "~")
            {
                return 3;
            }
            return 0;
        }
        public static DelegateBreukenBewerking toDelegate(string operand)
        {
            DelegateBreukenBewerking functie = null;
            switch (operand)
            {
                case "+":
                    functie = delegate(Breuk b1, Breuk b2)
                    { return b1 + b2; };
                    break;
                case "-":
                    functie = delegate(Breuk b1, Breuk b2)
                    { return b1 - b2; };
                    break;
                case "*":
                    functie = delegate(Breuk b1, Breuk b2)
                    { return b1 * b2; };
                    break;
                case ":":
                    functie = delegate(Breuk b1, Breuk b2)
                    { return b1 / b2; };
                    break;
            }
            return functie;
        }

    }
}
